#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <time.h>
#include <limits.h>
#include <stdalign.h>

#define LEN 81
#define SWAP(type, a, b)    do {type c=a; a=b; b=c;} while(0)

struct Element {
    unsigned font : 8;
    unsigned fsize : 7;
    unsigned : 0;
    unsigned align: 2;
    unsigned bold: 1;
    unsigned italic : 1;
    unsigned undline: 1;
};

int btoi(const char *);
int bitset(int);
int bisset(int h, unsigned);
unsigned rotate_l(unsigned, unsigned);

char * itobs(int, char *);
void print_bin(char *);

void about_bitfield(void);
void show_bitfield(struct Element *);
char bf_menu(void);

char * s_gets(char *, int);
void eatline(void);


char * aligns[3] = {"left", "center", "right"};
char * swi[2] = {"off", "on"};

int main(void)
{

    char bs[CHAR_BIT * sizeof(int) + 1];

    char * a = "10011";
    char * b = "111";
    char * c = "101";
    char * d = "1101";
    char * e = "1111111";
    char * f = "10000000";
    char * g = "1111111111111111";

    printf("a:%-16s decimal:%d\n", a, btoi(a));
    printf("b:%-16s decimal:%d\n", b, btoi(b));
    printf("c:%-16s decimal:%d\n", c, btoi(c));
    printf("d:%-16s decimal:%d\n", d, btoi(d));
    printf("e:%-16s decimal:%d\n", e, btoi(e));
    printf("f:%-16s decimal:%d\n", f, btoi(f));
    printf("g:%-16s decimal:%d\n", g, btoi(g));

    puts("number of setbit:");
    int h = 100;
    int j = 3;
    int k = 12;
    int l = 65543;
    int m = 0;
    int n = 1;
    int o = 128;
    int p = 127;

    itobs(h, bs); printf("\nd:%-8d num:%-3d bin: ", h, bitset(h)); print_bin(bs);
    itobs(j, bs); printf("\nd:%-8d num:%-3d bin: ", j, bitset(j)); print_bin(bs);
    itobs(k, bs); printf("\nd:%-8d num:%-3d bin: ", k, bitset(k)); print_bin(bs);
    itobs(l, bs); printf("\nd:%-8d num:%-3d bin: ", l, bitset(l)); print_bin(bs);
    itobs(m, bs); printf("\nd:%-8d num:%-3d bin: ", m, bitset(m)); print_bin(bs);
    itobs(n, bs); printf("\nd:%-8d num:%-3d bin: ", n, bitset(n)); print_bin(bs);
    itobs(o, bs); printf("\nd:%-8d num:%-3d bin: ", o, bitset(o)); print_bin(bs);
    itobs(p, bs); printf("\nd:%-8d num:%-3d bin: ", p, bitset(p)); print_bin(bs);
    putchar('\n');

    puts("examine n'th bit:");
    int bb=3;

    itobs(h, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", h, bb, bisset(h,bb)); print_bin(bs);
    itobs(j, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", j, bb, bisset(j,bb)); print_bin(bs);
    itobs(k, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", k, bb, bisset(k,bb)); print_bin(bs);
    itobs(l, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", l, bb, bisset(l,bb)); print_bin(bs);
    itobs(m, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", m, bb, bisset(m,bb)); print_bin(bs);
    itobs(n, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", n, bb, bisset(n,bb)); print_bin(bs);
    itobs(o, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", o, bb, bisset(o,bb)); print_bin(bs);
    itobs(p, bs); printf("\nd:%-8d num:%-3d res:%d bin: ", p, bb, bisset(p,bb)); print_bin(bs);
    putchar('\n');


    puts("\n\nbinary vector rotate:");
    int num = 4;
    h = (1 << 31);
    j = (1 << 29) + (2 | 32);
    k = (1 << 30) + (1 | 4 | 6);
    l = (1 << 31);
    m = (1 << 31) + 1;
    n = (1 << 30) + 2;
    o = (1 << 29) + (1 | 2 | 8);
    p = (1 << 28) + (2 | 4 | 8 | 32);

    itobs(h, bs); printf("\nd:%-8d num:%-3d\nbefore: ", h, num); print_bin(bs); printf("\n after: "); h = rotate_l(h, num); itobs(h, bs); print_bin(bs);
    itobs(j, bs); printf("\nd:%-8d num:%-3d\nbefore: ", j, num); print_bin(bs); printf("\n after: "); j = rotate_l(j, num); itobs(j, bs); print_bin(bs);
    itobs(k, bs); printf("\nd:%-8d num:%-3d\nbefore: ", k, num); print_bin(bs); printf("\n after: "); k = rotate_l(k, num); itobs(k, bs); print_bin(bs);
    itobs(l, bs); printf("\nd:%-8d num:%-3d\nbefore: ", l, num); print_bin(bs); printf("\n after: "); l = rotate_l(l, num); itobs(l, bs); print_bin(bs);
    itobs(m, bs); printf("\nd:%-8d num:%-3d\nbefore: ", m, num); print_bin(bs); printf("\n after: "); m = rotate_l(m, num); itobs(m, bs); print_bin(bs);
    itobs(n, bs); printf("\nd:%-8d num:%-3d\nbefore: ", n, num); print_bin(bs); printf("\n after: "); n = rotate_l(n, num); itobs(n, bs); print_bin(bs);
    itobs(o, bs); printf("\nd:%-8d num:%-3d\nbefore: ", o, num); print_bin(bs); printf("\n after: "); o = rotate_l(o, num); itobs(o, bs); print_bin(bs);
    itobs(p, bs); printf("\nd:%-8d num:%-3d\nbefore: ", p, num); print_bin(bs); printf("\n after: "); p = rotate_l(p, num); itobs(p, bs); print_bin(bs);
    putchar('\n');

    puts("\n\nbitfield example:");
    about_bitfield();

    return 0;
}

void about_bitfield(void)
{
    char choice;
    int fmask = (1 << 8) - 1;
    int smask = (1 << 7) - 1;
    int input;

    char alis[2];
    char alic;

    struct Element elem = {10, 12, 0, 0, 0, 0};
    show_bitfield(&elem);

    while ((choice = bf_menu()) != 'q')
    {
        switch (choice)
        {
            case 'f':
                puts("Enter font ID (0-255): ");
                while (scanf("%d", &input) != 1)
                    puts("Please enter integer.");

                eatline();

                elem.font = fmask & input;
                break;

            case 's':
                puts("Enter font size (0-127): ");
                while (scanf("%d", &input) != 1)
                    puts("Please enter integer.");

                eatline();
                elem.fsize = smask & input;
                break;

            case 'a':
                puts("Select alignment:");
                puts("l)left   c)center   r)right");

                while (s_gets(alis, 2)) {
                    alic = tolower(alis[0]);

                    if (strchr("lcr", alic) == NULL)
                        puts("Please choice l c or r.");
                    else
                        break;
                }

                elem.align = alic == 'l'? 0: (alic == 'c'? 1: 2);
                break;

            case 'i':
                elem.italic ^= 01; break;
            case 'b':
                elem.bold   ^= 01; break;
            case 'u':
                elem.undline ^= 01; break;
        }

        if (choice == 'q')
            break;

        show_bitfield(&elem);

    }

    puts("Bye!");
}

void show_bitfield(struct Element * elem)
{
    printf("ID  SIZE  ALIGNMENT   B   I   U\n");
    printf("%-3d %3d %8s %7s %3s %3s\n\n",
            elem->font,
            elem->fsize,
            aligns[elem->align],
            swi[elem->bold],
            swi[elem->italic],
            swi[elem->undline]);


}

char bf_menu(void)
{
    char ch;
    printf("f) change font    s) change size    a)change  alignment\n");
    printf("b) toggle bold    i) toggle italic  u)toggle underline\n");
    printf("q) quit\n");

    while ((ch = getchar())) {
        eatline();

        if (strchr("fsabiuq", ch) == NULL) {
            puts("Please enter correct choice.");
        } else
            break;
    }

    return tolower(ch);
}

int btoi(const char * ch)
{
    int ret = 0;
    int n = strlen(ch);
    int b = 1;

    for (--n; n>=0; --n, b <<= 1)
    {
        ret += (ch[n] - '0') * b;
    }

    return ret;
}

//from lower to high. the lowest is 1 not 0
int bisset(int h, unsigned pos)
{
    int mask = 1 << (pos - 1);
    return (h & mask) == mask;

}

int bitset(int n)
{
    int ret = 0;
    while (n) {
        ret += (n & 01);
        n >>= 1;
    }
    return ret;
}

unsigned rotate_l(unsigned x, unsigned n)
{
    int size = CHAR_BIT * sizeof(unsigned);
    unsigned mask = 1 << (size - 1);

    for (; n>0; --n)
        x = (x << 1) + ((x & mask) == mask);

    return x;
}

char * itobs(int n, char * ps)
{
    int i;
    int size = CHAR_BIT * sizeof(int);

    for (i=size-1; i>=0; --i, n>>=1)
        ps[i] = '0' + (01 & n);

    ps[size] = 0;

    return ps;
}

void print_bin(char * s)
{
    int i = 0;
    while (*s)
    {
        putchar(*s++);
        if (++i % 4 == 0)
            putchar(' ');
    }
}

char * s_gets(char * str, int n)
{
    char * ret_val;
    char * find;

    if ((ret_val = fgets(str, n, stdin))) {
        find = strchr(str, '\n');
        if (find)
            *find = 0;
        else
            while (getchar() != '\n')
                ;
    }

    return ret_val;
}

void eatline(void)
{
    while (getchar() != '\n')
        ;
}
